Load libraries

Load WQ and site info data into Environment

CRB_qw_040519 <- readRDS("D:/Sepulveda_USGS/BOR_mussel_waterquality/DreissenidWaterQuality/Data/raw/CRB_qw_040519.rds")
CRB_qw_siteinfo_040519 <- readRDS("D:/Sepulveda_USGS/BOR_mussel_waterquality/DreissenidWaterQuality/Data/raw/CRB_qw_siteinfo_040519.rds")
  1. Simple cleanup of the data
#To load already saved data, find file in directory and click on it
CRB_qw = CRB_qw_040519 %>% 
  filter(ActivityMediaName == "Water") %>% 
    filter(!is.na(ResultMeasureValue)) %>%  # get rid of NA values
    mutate(ResultMeasure.MeasureUnitCode = 
             str_trim(ResultMeasure.MeasureUnitCode, side = "both")   #get rid of white spaces
          ) %>%  
    filter(ActivityMediaName == "Water") %>% 
    filter(!is.na(ResultMeasureValue)) %>%  # get rid of NA values
    mutate(ResultMeasure.MeasureUnitCode = 
             str_trim(ResultMeasure.MeasureUnitCode, side = "both")   #get rid of white spaces
          ) %>% 
  select(OrganizationFormalName,
         ActivityStartDate, ActivityStartTime.Time, ActivityStartTime.TimeZoneCode,
         ActivityDepthHeightMeasure.MeasureValue, ActivityDepthHeightMeasure.MeasureUnitCode,
         CharacteristicName,
         MonitoringLocationIdentifier,
         SampleCollectionMethod.MethodName, SampleCollectionEquipmentName,
         ResultMeasureValue, ResultMeasure.MeasureUnitCode, ResultSampleFractionText,
         ResultAnalyticalMethod.MethodName, MethodDescriptionText,
         ProviderName)
siteInfo=CRB_qw_siteinfo_040519

Split dataset by ‘charactersticName’ and just cleanup and explore pH

CRB_pH = as_tibble(CRB_qw)%>% 
   filter(CharacteristicName == "pH")
ggplot(CRB_pH, aes(x = ResultMeasure.MeasureUnitCode, y = ResultMeasureValue))+
  geom_boxplot()

That’s awful. Let’s first limit pH values to 0 -14

CRB_pH = as_tibble(CRB_qw)%>% 
    filter(CharacteristicName == "pH") %>% 
    filter(!(ResultMeasureValue > 14|ResultMeasureValue <0))
ggplot(CRB_pH, aes(x = ResultMeasure.MeasureUnitCode, y = ResultMeasureValue))+
  geom_boxplot()

Better. Let’s assume that these values are correct, but the units are wrong

CRB_pH = as_tibble(CRB_qw)%>% 
    filter(CharacteristicName == "pH") %>% 
    filter(!(ResultMeasureValue > 14|ResultMeasureValue <0)) %>%
    mutate(ResultMeasure.MeasureUnitCode, ResultMeasure.MeasureUnitCode =   
          ifelse(ResultMeasure.MeasureUnitCode %in% 
                   c("deg C", "NTU", "nu", "%", "mg/l",
                   "Mole/l", "std units", "ug/l",
                   "units/cm"), "None",
                 ResultMeasure.MeasureUnitCode))
  
ggplot(CRB_pH, aes(x = ResultMeasure.MeasureUnitCode, y = ResultMeasureValue))+
  geom_boxplot()

Okay, let’s call that good for now and start exploring the data. Here is the pH script that consolidates all of the previous scripts and adds a month column for each observation.

CRB_pH = CRB_qw %>% 
    filter(CharacteristicName == "pH") %>%
    filter(!(ResultMeasureValue > 14|ResultMeasureValue <0)) %>% 
    mutate(ResultMeasure.MeasureUnitCode, ResultMeasure.MeasureUnitCode =   
             ifelse(ResultMeasure.MeasureUnitCode %in%
                     c("deg C", "NTU", "nu", "%", "mg/l",
                           "Mole/l", "std units", "ug/l", "units/cm"), "None",
                    ResultMeasure.MeasureUnitCode)) %>%
    mutate(Month = lubridate::month(ActivityStartDate, label=TRUE)) 

When are pH samples collected? Most are in summer… do we want to restrict pH to non-winter (i.e., mussel growing season)?

ggplot(CRB_pH, aes(x = Month))+
  geom_histogram(stat="count")

Thinking more about how to filter pH data… does month of sampling matter? Mean stdev per month doesn’t change much for the sites where 12 months data are available.

CRB_pH_months= CRB_qw %>% 
  filter(CharacteristicName == "pH") %>%
  filter(!(ResultMeasureValue > 14|ResultMeasureValue <0)) %>% 
  mutate(ResultMeasure.MeasureUnitCode, ResultMeasure.MeasureUnitCode =   
           ifelse(ResultMeasure.MeasureUnitCode %in%
                    c("deg C", "NTU", "nu", "%", "mg/l",
                      "Mole/l", "std units", "ug/l", "units/cm"), "None",
                  ResultMeasure.MeasureUnitCode)) %>%
  mutate(Month = month(ActivityStartDate, label=TRUE)) %>% 
  group_by(MonitoringLocationIdentifier) %>%
  mutate(count = n_distinct(Month)) %>% #count how many unique months with pH data at a site
  filter(count == 12) %>% #limit data to just sites where data was collected every month
  group_by(MonitoringLocationIdentifier, Month) %>%  #summarize data at each site for each month
  summarise(max = max(ResultMeasureValue, na.rm = TRUE),
            mean = mean(ResultMeasureValue, na.rm = TRUE),
            stdev = sd(ResultMeasureValue, na.rm = TRUE))
Warning messages:
1: In get(x, envir = ns) : restarting interrupted promise evaluation
2: In get(x, envir = ns) : restarting interrupted promise evaluation
3: In get(x, envir = ns) : restarting interrupted promise evaluation
4: In get(x, envir = ns) : restarting interrupted promise evaluation
5: In get(x, envir = ns) : restarting interrupted promise evaluation
6: In get(x, envir = ns) : restarting interrupted promise evaluation
7: In get(results[[i]], pos = which(search() == packages[[i]])) :
  restarting interrupted promise evaluation
8: In get(results[[i]], pos = which(search() == packages[[i]])) :
  restarting interrupted promise evaluation
9: In get(results[[i]], pos = which(search() == packages[[i]])) :
  restarting interrupted promise evaluation
  
ggplot(CRB_pH_months, aes(x = Month, y = stdev))+
  geom_boxplot()

But moving forward, let’s code June, July, August and September samples as “High Quality” and all others as “Low Quality”

CRB_pH_monthsquality= CRB_qw %>% 
  filter(CharacteristicName == "pH") %>%
  filter(!(ResultMeasureValue > 14|ResultMeasureValue <0)) %>% 
  mutate(ResultMeasure.MeasureUnitCode, ResultMeasure.MeasureUnitCode =   
           ifelse(ResultMeasure.MeasureUnitCode %in%
                    c("deg C", "NTU", "nu", "%", "mg/l",
                      "Mole/l", "std units", "ug/l", "units/cm"), "None",
                  ResultMeasure.MeasureUnitCode)) %>%
  mutate(Month = month(ActivityStartDate, label=TRUE)) %>% 
  mutate(MonthQuality = ifelse(Month %in% c("Jun", "Jul", "Aug", "Sep"), "HighQuality", "LowQuality"))

How many samples have been collected at each site? Let’s group the data by site and calculate some summaries

CRB_pH_summ = CRB_pH_monthsquality%>%
  filter(MonthQuality == "HighQuality") %>% 
group_by(MonitoringLocationIdentifier) %>%
      summarise(count=n(),
                start=min(ActivityStartDate),
                end=max(ActivityStartDate),
                max = max(ResultMeasureValue, na.rm = TRUE),
                mean = mean(ResultMeasureValue, na.rm = TRUE),
                stdev = sd(ResultMeasureValue, na.rm = TRUE))

Dot plot to view the # of data points at a site relative to pH stdeviation. Do we want to get rid of sites with a stdev > 1?

ggplot(CRB_pH_summ, aes(x = count, y = stdev))+
  geom_point()+
  scale_y_continuous(limits = c(0,5))

Wow, some sites have been visited more than 1000 times. What are these sites? Seems to be a mix of long-term monitoring and continuous sondes.

CRB_pH_summ %>% 
  arrange(desc(count)) %>% 
  head(n = 20)  # look at the top 20 sites

Look at data spatially using leaflet. First, join spatial infor from siteInfo

CRB_pH = CRB_qw %>% 
    filter(CharacteristicName == "pH") %>%
    filter(!(ResultMeasureValue > 14|ResultMeasureValue <0)) %>% 
    mutate(ResultMeasure.MeasureUnitCode, ResultMeasure.MeasureUnitCode =   
             ifelse(ResultMeasure.MeasureUnitCode %in%
                     c("deg C", "NTU", "nu", "%", "mg/l",
                           "Mole/l", "std units", "ug/l", "units/cm"), "None",
                    ResultMeasure.MeasureUnitCode)) %>%
    mutate(Month = month(ActivityStartDate, label=TRUE)) %>% 
    mutate(MonthQuality = ifelse(Month %in% c("Jun", "Jul", "Aug", "Sep"), "HighQuality", "LowQuality")) %>% 
    filter(MonthQuality == "HighQuality") %>%
  group_by(MonitoringLocationIdentifier) %>%
      summarise(count=n(),
                start=min(ActivityStartDate),
                end=max(ActivityStartDate),
                max = max(ResultMeasureValue, na.rm = TRUE),
                mean = mean(ResultMeasureValue, na.rm = TRUE),
                stdev = sd(ResultMeasureValue, na.rm = TRUE)) %>% 
    left_join(siteInfo, by = "MonitoringLocationIdentifier") # join location information

Next enter parameters for mapping on leaflet. You can change pH breakpoints in the risk.bins line.

col_types <- c("darkblue","orange","red","brown","pink","dodgerblue")  
risk.bins = c(0, 6.59, 6.99, 7.51, 7.99, 8.51, 14)
risk.pal = colorBin(col_types, bins = risk.bins, na.color = "#aaff56")
rad <-3*seq(1,4,length.out = 16)
CRB_pH$sizes <- rad[as.numeric(cut(CRB_pH$count, breaks=16))]

Finally, plot it spatially via leaflet

library(mapview)
phMap = leaflet(data=CRB_pH) %>% 
      #addTiles() %>% for simple openstreet basemap
      addProviderTiles(providers$Esri.NatGeoWorldMap) %>%  
      addCircleMarkers(~dec_lon_va, ~dec_lat_va,
                       fillColor = ~risk.pal(max),
                       radius = 2,  # to make size a function of count replace with ~sizes
                       fillOpacity = 0.8, opacity = 0.8,stroke=FALSE,
                       popup=~station_nm) %>%
      addLegend(position = 'bottomleft',
                pal=risk.pal,
                values=~max,
                opacity = 0.8,
                labFormat = labelFormat(digits = 1), 
                title = 'Max pH Value') %>% 
  addMouseCoordinates(style = "basic") %>% 
  setView(lng = -118.3,
          lat = 45.6,
          zoom = 5.45)
phMap

Explore the calcium data

CRB_Ca = CRB_qw %>% 
    filter(CharacteristicName == "Calcium")
ggplot(CRB_Ca, aes(x = ResultMeasure.MeasureUnitCode, y = ResultMeasureValue))+
  geom_boxplot()

Appears to have a little of different units, so let’s clean it up. 1. Limit data to just ‘dissolved’ and ‘total’. 2. Covert ug/l to mg/l

CRB_Ca = CRB_qw %>% 
  filter(CharacteristicName == "Calcium") %>% 
  
  #get rid of white spaces
  mutate(ResultMeasure.MeasureUnitCode = 
             str_trim(ResultMeasure.MeasureUnitCode, side = "both")) %>%   
  #limit to just dissolved and total Ca
  filter(ResultSampleFractionText %in% c("Dissolved", "Total")) %>%   
  
  # convert ug/l to mg/l
  mutate(ResultMeasureValue = case_when(ResultMeasure.MeasureUnitCode == "ug/l" ~ (ResultMeasureValue/1000),TRUE~ as.numeric(ResultMeasureValue))) %>% 
    
  # recode ug/l to mg/l
  mutate(ResultMeasure.MeasureUnitCode, ResultMeasure.MeasureUnitCode =
             ifelse(ResultMeasure.MeasureUnitCode == "ug/l", "mg/l",
                    ResultMeasure.MeasureUnitCode))
  
ggplot(CRB_Ca, aes(x = ResultMeasure.MeasureUnitCode, y = ResultMeasureValue))+
  geom_boxplot()

Better, but still a lot of units that are not mg/l

unique(CRB_Ca$ResultMeasure.MeasureUnitCode)
[1] "mg/l"       "ppm"        "ueq/L"      "mg/kg"      "mg/l CaCO3"

Let’s only keep value that have mg/l units and add a month column. We may want to consider calcium carbonate (CaCO3) for future analyses though.

CRB_Ca = CRB_qw %>% 
  filter(CharacteristicName == "Calcium") %>% 
  
  #get rid of white spaces
  mutate(ResultMeasure.MeasureUnitCode = 
             str_trim(ResultMeasure.MeasureUnitCode, side = "both")) %>%   
  #limit to just dissolved and total Ca
  filter(ResultSampleFractionText %in% c("Dissolved", "Total")) %>%   
  
  # convert ug/l to mg/l
  mutate(ResultMeasureValue = case_when(ResultMeasure.MeasureUnitCode == "ug/l" ~ (ResultMeasureValue/1000),TRUE~ as.numeric(ResultMeasureValue))) %>% 
    
  # recode ug/l to mg/l
  mutate(ResultMeasure.MeasureUnitCode, ResultMeasure.MeasureUnitCode =
             ifelse(ResultMeasure.MeasureUnitCode == "ug/l", "mg/l",
                    ResultMeasure.MeasureUnitCode)) %>% 
  
  #limit to just mg/l
  filter(ResultMeasure.MeasureUnitCode =="mg/l") %>% 
  
  #add a Month column in case we want to evaluate sampling timing
    mutate(Month = month(ActivityStartDate, label=TRUE))
  
ggplot(CRB_Ca, aes(x = ResultMeasure.MeasureUnitCode, y = ResultMeasureValue))+
  geom_boxplot()

Should we get ride of outliers? Here, I removed any value > 97.5th percentile

CRB_Ca_out = CRB_Ca %>%
  filter(!ResultMeasureValue > quantile(ResultMeasureValue, 0.975)) 
ggplot(CRB_Ca_out, aes(x = ResultMeasure.MeasureUnitCode, y = ResultMeasureValue))+
  geom_boxplot()

When are Ca samples collected? Most are in summer… do we want to restrict Ca to non-winter (i.e., mussel growing season)?

ggplot(CRB_Ca_out, aes(x = Month))+
  geom_histogram(stat="count")

How many samples have been collected at each site? Let’s group the data by site and calculate some summaries

CRB_Ca_summ = CRB_Ca_out%>% 
group_by(MonitoringLocationIdentifier) %>%
      summarise(count=n(),
                start=min(ActivityStartDate),
                end=max(ActivityStartDate),
                max = max(ResultMeasureValue, na.rm = TRUE),
                mean = mean(ResultMeasureValue, na.rm = TRUE),
                stdev = sd(ResultMeasureValue, na.rm = TRUE))

Dot plot to view the # of data points at a site relative to pH stdeviation. Do we want to get rid of sites with a stdev > 1?

ggplot(CRB_Ca_summ, aes(x = count, y = stdev))+
  geom_point()+
  scale_y_continuous(limits = c(0,60))

Look at data spatially using leaflet. Put all the critical code together, then join site info.

CRB_Ca = CRB_qw %>% 
  filter(CharacteristicName == "Calcium") %>% 
  mutate(ResultMeasure.MeasureUnitCode = 
             str_trim(ResultMeasure.MeasureUnitCode, side = "both")) %>%   
  filter(ResultSampleFractionText %in% c("Dissolved", "Total")) %>%   
  mutate(ResultMeasureValue = case_when(ResultMeasure.MeasureUnitCode == "ug/l" 
         ~ (ResultMeasureValue/1000),TRUE~ as.numeric(ResultMeasureValue))) %>% 
  mutate(ResultMeasure.MeasureUnitCode, ResultMeasure.MeasureUnitCode =
             ifelse(ResultMeasure.MeasureUnitCode == "ug/l", "mg/l",
                    ResultMeasure.MeasureUnitCode)) %>% 
  filter(ResultMeasure.MeasureUnitCode =="mg/l") %>%
  filter(!ResultMeasureValue > quantile(ResultMeasureValue, 0.975)) %>% 
  mutate(Month = month(ActivityStartDate, label=TRUE)) %>% 
  mutate(MonthQuality = ifelse(Month %in% c("Jun", "Jul", "Aug", "Sep"), "HighQuality", "LowQuality")) %>% 
    filter(MonthQuality == "HighQuality") %>%  #limit data to summer months
  group_by(MonitoringLocationIdentifier) %>%
      summarise(count=n(),
                start=min(ActivityStartDate),
                end=max(ActivityStartDate),
                max = max(ResultMeasureValue, na.rm = TRUE),
                mean = mean(ResultMeasureValue, na.rm = TRUE)) %>% 
    left_join(siteInfo, by = "MonitoringLocationIdentifier") # join location information

Next enter parameters for mapping on leaflet. You can change Ca breakpoints in the risk.bins line. I followed Whittier et al. 2008, with who “defined risk based on calcium concentrations as: very low (< 12 mg L???1), low (12-20 mg L???1), moderate (20-28 mg L???1), and high (> 28 mg L???1)”.

col_types <- c("darkblue","pink", "orange","red")  
risk.bins = c(0, 11.99, 19.99, 27.99, 20000)
risk.pal = colorBin(col_types, bins = risk.bins, na.color = "#aaff56")
rad <-3*seq(1,4,length.out = 16)
CRB_Ca$sizes <- rad[as.numeric(cut(CRB_Ca$count, breaks=16))]

Finally, plot it spatially via leaflet

CaMap = leaflet(data=CRB_Ca) %>% 
      #addTiles() %>% for simple openstreet basemap
      addProviderTiles(providers$Esri.NatGeoWorldMap) %>%  #This is cool... https://rstudio.github.io/leaflet/basemaps.html
      addCircleMarkers(~dec_lon_va,~dec_lat_va,
                       fillColor = ~risk.pal(max),
                       radius = 2,  # to make size a function of count replace with ~sizes
                       fillOpacity = 0.8, opacity = 0.8,stroke=FALSE,
                       popup=~station_nm) %>%
      addLegend(position = 'bottomleft',
                pal=risk.pal,
                values=~max,
                opacity = 0.8,
                labFormat = labelFormat(digits = 1), 
                title = 'Max Ca Value') %>% 
  mapview::addMouseCoordinates(style = "basic") %>% 
  setView(lng = -118.3,
          lat = 45.6,
          zoom = 5.45)
CaMap

My attempt to look at both the pH and Ca maps

latticeview(CaMap, phMap)

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).

The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.

LS0tDQp0aXRsZTogIkV4cGxvcmUgJiBGaWx0ZXIgQ1JCIHdhdGVyIGRhdGEiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGAgDQoNCkxvYWQgbGlicmFyaWVzDQpgYGB7ciBpbmNsdWRlPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCg0KcGFja3MgPC0gYygndGlkeXZlcnNlJywgJ2RhdGFSZXRyaWV2YWwnLCAncmVzaGFwZTInLCAnc2NhbGVzJywgJ3N0cmluZ3InLA0KICAgICAgICAgICAnbGVhZmxldCcsICdrbml0cicsICdsdWJyaWRhdGUnKQ0KbGFwcGx5KHBhY2tzLCByZXF1aXJlLCBjaGFyYWN0ZXIub25seSA9IFQpDQpgYGANCg0KTG9hZCBXUSBhbmQgc2l0ZSBpbmZvIGRhdGEgaW50byBFbnZpcm9ubWVudA0KYGBge3IgZWNobz1ULCByZXN1bHRzPSdoaWRlJ30NCkNSQl9xd18wNDA1MTkgPC0gcmVhZFJEUygiRDovU2VwdWx2ZWRhX1VTR1MvQk9SX211c3NlbF93YXRlcnF1YWxpdHkvRHJlaXNzZW5pZFdhdGVyUXVhbGl0eS9EYXRhL3Jhdy9DUkJfcXdfMDQwNTE5LnJkcyIpDQoNCkNSQl9xd19zaXRlaW5mb18wNDA1MTkgPC0gcmVhZFJEUygiRDovU2VwdWx2ZWRhX1VTR1MvQk9SX211c3NlbF93YXRlcnF1YWxpdHkvRHJlaXNzZW5pZFdhdGVyUXVhbGl0eS9EYXRhL3Jhdy9DUkJfcXdfc2l0ZWluZm9fMDQwNTE5LnJkcyIpDQpgYGANCg0KDQoNCg0KMi4gU2ltcGxlIGNsZWFudXAgb2YgdGhlIGRhdGENCmBgYHtyIGVjaG89VCwgcmVzdWx0cz0naGlkZSd9DQojVG8gbG9hZCBhbHJlYWR5IHNhdmVkIGRhdGEsIGZpbmQgZmlsZSBpbiBkaXJlY3RvcnkgYW5kIGNsaWNrIG9uIGl0DQpDUkJfcXcgPSBDUkJfcXdfMDQwNTE5ICU+JSANCiAgZmlsdGVyKEFjdGl2aXR5TWVkaWFOYW1lID09ICJXYXRlciIpICU+JSANCiAgICBmaWx0ZXIoIWlzLm5hKFJlc3VsdE1lYXN1cmVWYWx1ZSkpICU+JSAgIyBnZXQgcmlkIG9mIE5BIHZhbHVlcw0KICAgIG11dGF0ZShSZXN1bHRNZWFzdXJlLk1lYXN1cmVVbml0Q29kZSA9IA0KICAgICAgICAgICAgIHN0cl90cmltKFJlc3VsdE1lYXN1cmUuTWVhc3VyZVVuaXRDb2RlLCBzaWRlID0gImJvdGgiKSAgICNnZXQgcmlkIG9mIHdoaXRlIHNwYWNlcw0KICAgICAgICAgICkgJT4lICANCiAgICBmaWx0ZXIoQWN0aXZpdHlNZWRpYU5hbWUgPT0gIldhdGVyIikgJT4lIA0KICAgIGZpbHRlcighaXMubmEoUmVzdWx0TWVhc3VyZVZhbHVlKSkgJT4lICAjIGdldCByaWQgb2YgTkEgdmFsdWVzDQogICAgbXV0YXRlKFJlc3VsdE1lYXN1cmUuTWVhc3VyZVVuaXRDb2RlID0gDQogICAgICAgICAgICAgc3RyX3RyaW0oUmVzdWx0TWVhc3VyZS5NZWFzdXJlVW5pdENvZGUsIHNpZGUgPSAiYm90aCIpICAgI2dldCByaWQgb2Ygd2hpdGUgc3BhY2VzDQogICAgICAgICAgKSAlPiUgDQogIHNlbGVjdChPcmdhbml6YXRpb25Gb3JtYWxOYW1lLA0KICAgICAgICAgQWN0aXZpdHlTdGFydERhdGUsIEFjdGl2aXR5U3RhcnRUaW1lLlRpbWUsIEFjdGl2aXR5U3RhcnRUaW1lLlRpbWVab25lQ29kZSwNCiAgICAgICAgIEFjdGl2aXR5RGVwdGhIZWlnaHRNZWFzdXJlLk1lYXN1cmVWYWx1ZSwgQWN0aXZpdHlEZXB0aEhlaWdodE1lYXN1cmUuTWVhc3VyZVVuaXRDb2RlLA0KICAgICAgICAgQ2hhcmFjdGVyaXN0aWNOYW1lLA0KICAgICAgICAgTW9uaXRvcmluZ0xvY2F0aW9uSWRlbnRpZmllciwNCiAgICAgICAgIFNhbXBsZUNvbGxlY3Rpb25NZXRob2QuTWV0aG9kTmFtZSwgU2FtcGxlQ29sbGVjdGlvbkVxdWlwbWVudE5hbWUsDQogICAgICAgICBSZXN1bHRNZWFzdXJlVmFsdWUsIFJlc3VsdE1lYXN1cmUuTWVhc3VyZVVuaXRDb2RlLCBSZXN1bHRTYW1wbGVGcmFjdGlvblRleHQsDQogICAgICAgICBSZXN1bHRBbmFseXRpY2FsTWV0aG9kLk1ldGhvZE5hbWUsIE1ldGhvZERlc2NyaXB0aW9uVGV4dCwNCiAgICAgICAgIFByb3ZpZGVyTmFtZSkNCg0Kc2l0ZUluZm89Q1JCX3F3X3NpdGVpbmZvXzA0MDUxOQ0KDQpgYGANCg0KDQpTcGxpdCBkYXRhc2V0IGJ5ICdjaGFyYWN0ZXJzdGljTmFtZScgYW5kIGp1c3QgY2xlYW51cCBhbmQgZXhwbG9yZSBwSA0KYGBge3J9DQpDUkJfcEggPSBhc190aWJibGUoQ1JCX3F3KSU+JSANCiAgIGZpbHRlcihDaGFyYWN0ZXJpc3RpY05hbWUgPT0gInBIIikNCg0KZ2dwbG90KENSQl9wSCwgYWVzKHggPSBSZXN1bHRNZWFzdXJlLk1lYXN1cmVVbml0Q29kZSwgeSA9IFJlc3VsdE1lYXN1cmVWYWx1ZSkpKw0KICBnZW9tX2JveHBsb3QoKQ0KYGBgDQogIA0KICANClRoYXQncyBhd2Z1bC4gTGV0J3MgZmlyc3QgbGltaXQgcEggdmFsdWVzIHRvIDAgLTE0DQpgYGB7cn0NCkNSQl9wSCA9IGFzX3RpYmJsZShDUkJfcXcpJT4lIA0KICAgIGZpbHRlcihDaGFyYWN0ZXJpc3RpY05hbWUgPT0gInBIIikgJT4lIA0KICAgIGZpbHRlcighKFJlc3VsdE1lYXN1cmVWYWx1ZSA+IDE0fFJlc3VsdE1lYXN1cmVWYWx1ZSA8MCkpDQoNCmdncGxvdChDUkJfcEgsIGFlcyh4ID0gUmVzdWx0TWVhc3VyZS5NZWFzdXJlVW5pdENvZGUsIHkgPSBSZXN1bHRNZWFzdXJlVmFsdWUpKSsNCiAgZ2VvbV9ib3hwbG90KCkNCg0KYGBgDQoNCg0KQmV0dGVyLiBMZXQncyBhc3N1bWUgdGhhdCB0aGVzZSB2YWx1ZXMgYXJlIGNvcnJlY3QsIGJ1dCB0aGUgdW5pdHMgYXJlIHdyb25nDQpgYGB7cn0NCkNSQl9wSCA9IGFzX3RpYmJsZShDUkJfcXcpJT4lIA0KICAgIGZpbHRlcihDaGFyYWN0ZXJpc3RpY05hbWUgPT0gInBIIikgJT4lIA0KICAgIGZpbHRlcighKFJlc3VsdE1lYXN1cmVWYWx1ZSA+IDE0fFJlc3VsdE1lYXN1cmVWYWx1ZSA8MCkpICU+JQ0KICAgIG11dGF0ZShSZXN1bHRNZWFzdXJlLk1lYXN1cmVVbml0Q29kZSwgUmVzdWx0TWVhc3VyZS5NZWFzdXJlVW5pdENvZGUgPSAgIA0KICAgICAgICAgIGlmZWxzZShSZXN1bHRNZWFzdXJlLk1lYXN1cmVVbml0Q29kZSAlaW4lIA0KICAgICAgICAgICAgICAgICAgIGMoImRlZyBDIiwgIk5UVSIsICJudSIsICIlIiwgIm1nL2wiLA0KICAgICAgICAgICAgICAgICAgICJNb2xlL2wiLCAic3RkIHVuaXRzIiwgInVnL2wiLA0KICAgICAgICAgICAgICAgICAgICJ1bml0cy9jbSIpLCAiTm9uZSIsDQogICAgICAgICAgICAgICAgIFJlc3VsdE1lYXN1cmUuTWVhc3VyZVVuaXRDb2RlKSkNCiAgDQoNCmdncGxvdChDUkJfcEgsIGFlcyh4ID0gUmVzdWx0TWVhc3VyZS5NZWFzdXJlVW5pdENvZGUsIHkgPSBSZXN1bHRNZWFzdXJlVmFsdWUpKSsNCiAgZ2VvbV9ib3hwbG90KCkNCmBgYA0KDQoNCiAgT2theSwgbGV0J3MgY2FsbCB0aGF0IGdvb2QgZm9yIG5vdyBhbmQgc3RhcnQgZXhwbG9yaW5nIHRoZSBkYXRhLiBIZXJlIGlzIHRoZSBwSCBzY3JpcHQgdGhhdCBjb25zb2xpZGF0ZXMgYWxsIG9mIHRoZSBwcmV2aW91cyBzY3JpcHRzIGFuZCBhZGRzIGEgbW9udGggY29sdW1uIGZvciBlYWNoIG9ic2VydmF0aW9uLiANCmBgYHtyLCBldmFsPUZBTFNFfQ0KQ1JCX3BIID0gQ1JCX3F3ICU+JSANCiAgICBmaWx0ZXIoQ2hhcmFjdGVyaXN0aWNOYW1lID09ICJwSCIpICU+JQ0KICAgIGZpbHRlcighKFJlc3VsdE1lYXN1cmVWYWx1ZSA+IDE0fFJlc3VsdE1lYXN1cmVWYWx1ZSA8MCkpICU+JSANCiAgICBtdXRhdGUoUmVzdWx0TWVhc3VyZS5NZWFzdXJlVW5pdENvZGUsIFJlc3VsdE1lYXN1cmUuTWVhc3VyZVVuaXRDb2RlID0gICANCiAgICAgICAgICAgICBpZmVsc2UoUmVzdWx0TWVhc3VyZS5NZWFzdXJlVW5pdENvZGUgJWluJQ0KICAgICAgICAgICAgICAgICAgICAgYygiZGVnIEMiLCAiTlRVIiwgIm51IiwgIiUiLCAibWcvbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAiTW9sZS9sIiwgInN0ZCB1bml0cyIsICJ1Zy9sIiwgInVuaXRzL2NtIiksICJOb25lIiwNCiAgICAgICAgICAgICAgICAgICAgUmVzdWx0TWVhc3VyZS5NZWFzdXJlVW5pdENvZGUpKSAlPiUNCiAgICBtdXRhdGUoTW9udGggPSBsdWJyaWRhdGU6Om1vbnRoKEFjdGl2aXR5U3RhcnREYXRlLCBsYWJlbD1UUlVFKSkgDQpgYGANCg0KICAgDQpXaGVuIGFyZSBwSCBzYW1wbGVzIGNvbGxlY3RlZD8gTW9zdCBhcmUgaW4gc3VtbWVyLi4uIGRvIHdlIHdhbnQgdG8gcmVzdHJpY3QgcEggdG8gbm9uLXdpbnRlciAoaS5lLiwgbXVzc2VsIGdyb3dpbmcgc2Vhc29uKT8NCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcGFnZWQucHJpbnQ9RkFMU0V9DQpnZ3Bsb3QoQ1JCX3BILCBhZXMoeCA9IE1vbnRoKSkrDQogIGdlb21faGlzdG9ncmFtKHN0YXQ9ImNvdW50IikNCg0KYGBgDQoNClRoaW5raW5nIG1vcmUgYWJvdXQgaG93IHRvIGZpbHRlciBwSCBkYXRhLi4uIGRvZXMgbW9udGggb2Ygc2FtcGxpbmcgbWF0dGVyPyANCk1lYW4gc3RkZXYgcGVyIG1vbnRoIGRvZXNuJ3QgY2hhbmdlIG11Y2ggZm9yIHRoZSBzaXRlcyB3aGVyZSAxMiBtb250aHMgZGF0YSBhcmUgYXZhaWxhYmxlLiAgDQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0KQ1JCX3BIX21vbnRocz0gQ1JCX3F3ICU+JSANCiAgZmlsdGVyKENoYXJhY3RlcmlzdGljTmFtZSA9PSAicEgiKSAlPiUNCiAgZmlsdGVyKCEoUmVzdWx0TWVhc3VyZVZhbHVlID4gMTR8UmVzdWx0TWVhc3VyZVZhbHVlIDwwKSkgJT4lIA0KICBtdXRhdGUoUmVzdWx0TWVhc3VyZS5NZWFzdXJlVW5pdENvZGUsIFJlc3VsdE1lYXN1cmUuTWVhc3VyZVVuaXRDb2RlID0gICANCiAgICAgICAgICAgaWZlbHNlKFJlc3VsdE1lYXN1cmUuTWVhc3VyZVVuaXRDb2RlICVpbiUNCiAgICAgICAgICAgICAgICAgICAgYygiZGVnIEMiLCAiTlRVIiwgIm51IiwgIiUiLCAibWcvbCIsDQogICAgICAgICAgICAgICAgICAgICAgIk1vbGUvbCIsICJzdGQgdW5pdHMiLCAidWcvbCIsICJ1bml0cy9jbSIpLCAiTm9uZSIsDQogICAgICAgICAgICAgICAgICBSZXN1bHRNZWFzdXJlLk1lYXN1cmVVbml0Q29kZSkpICU+JQ0KICBtdXRhdGUoTW9udGggPSBtb250aChBY3Rpdml0eVN0YXJ0RGF0ZSwgbGFiZWw9VFJVRSkpICU+JSANCiAgZ3JvdXBfYnkoTW9uaXRvcmluZ0xvY2F0aW9uSWRlbnRpZmllcikgJT4lDQogIG11dGF0ZShjb3VudCA9IG5fZGlzdGluY3QoTW9udGgpKSAlPiUgI2NvdW50IGhvdyBtYW55IHVuaXF1ZSBtb250aHMgd2l0aCBwSCBkYXRhIGF0IGEgc2l0ZQ0KICBmaWx0ZXIoY291bnQgPT0gMTIpICU+JSAjbGltaXQgZGF0YSB0byBqdXN0IHNpdGVzIHdoZXJlIGRhdGEgd2FzIGNvbGxlY3RlZCBldmVyeSBtb250aA0KICBncm91cF9ieShNb25pdG9yaW5nTG9jYXRpb25JZGVudGlmaWVyLCBNb250aCkgJT4lICAjc3VtbWFyaXplIGRhdGEgYXQgZWFjaCBzaXRlIGZvciBlYWNoIG1vbnRoDQogIHN1bW1hcmlzZShtYXggPSBtYXgoUmVzdWx0TWVhc3VyZVZhbHVlLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgbWVhbiA9IG1lYW4oUmVzdWx0TWVhc3VyZVZhbHVlLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgc3RkZXYgPSBzZChSZXN1bHRNZWFzdXJlVmFsdWUsIG5hLnJtID0gVFJVRSkpDQogIA0KDQpnZ3Bsb3QoQ1JCX3BIX21vbnRocywgYWVzKHggPSBNb250aCwgeSA9IHN0ZGV2KSkrDQogIGdlb21fYm94cGxvdCgpDQoNCg0KYGBgDQoNCkJ1dCBtb3ZpbmcgZm9yd2FyZCwgbGV0J3MgY29kZSBKdW5lLCBKdWx5LCBBdWd1c3QgYW5kIFNlcHRlbWJlciBzYW1wbGVzIGFzICJIaWdoIFF1YWxpdHkiIGFuZCBhbGwgb3RoZXJzIGFzICJMb3cgUXVhbGl0eSIgDQpgYGB7cn0NCkNSQl9wSF9tb250aHNxdWFsaXR5PSBDUkJfcXcgJT4lIA0KICBmaWx0ZXIoQ2hhcmFjdGVyaXN0aWNOYW1lID09ICJwSCIpICU+JQ0KICBmaWx0ZXIoIShSZXN1bHRNZWFzdXJlVmFsdWUgPiAxNHxSZXN1bHRNZWFzdXJlVmFsdWUgPDApKSAlPiUgDQogIG11dGF0ZShSZXN1bHRNZWFzdXJlLk1lYXN1cmVVbml0Q29kZSwgUmVzdWx0TWVhc3VyZS5NZWFzdXJlVW5pdENvZGUgPSAgIA0KICAgICAgICAgICBpZmVsc2UoUmVzdWx0TWVhc3VyZS5NZWFzdXJlVW5pdENvZGUgJWluJQ0KICAgICAgICAgICAgICAgICAgICBjKCJkZWcgQyIsICJOVFUiLCAibnUiLCAiJSIsICJtZy9sIiwNCiAgICAgICAgICAgICAgICAgICAgICAiTW9sZS9sIiwgInN0ZCB1bml0cyIsICJ1Zy9sIiwgInVuaXRzL2NtIiksICJOb25lIiwNCiAgICAgICAgICAgICAgICAgIFJlc3VsdE1lYXN1cmUuTWVhc3VyZVVuaXRDb2RlKSkgJT4lDQogIG11dGF0ZShNb250aCA9IG1vbnRoKEFjdGl2aXR5U3RhcnREYXRlLCBsYWJlbD1UUlVFKSkgJT4lIA0KICBtdXRhdGUoTW9udGhRdWFsaXR5ID0gaWZlbHNlKE1vbnRoICVpbiUgYygiSnVuIiwgIkp1bCIsICJBdWciLCAiU2VwIiksICJIaWdoUXVhbGl0eSIsICJMb3dRdWFsaXR5IikpDQpgYGANCg0KDQoNCg0KSG93IG1hbnkgc2FtcGxlcyBoYXZlIGJlZW4gY29sbGVjdGVkIGF0IGVhY2ggc2l0ZT8gTGV0J3MgZ3JvdXAgdGhlIGRhdGEgYnkgc2l0ZSBhbmQgY2FsY3VsYXRlIHNvbWUgc3VtbWFyaWVzIA0KYGBge3IsIGV2YWw9RkFMU0V9DQpDUkJfcEhfc3VtbSA9IENSQl9wSF9tb250aHNxdWFsaXR5JT4lDQogIGZpbHRlcihNb250aFF1YWxpdHkgPT0gIkhpZ2hRdWFsaXR5IikgJT4lIA0KZ3JvdXBfYnkoTW9uaXRvcmluZ0xvY2F0aW9uSWRlbnRpZmllcikgJT4lDQogICAgICBzdW1tYXJpc2UoY291bnQ9bigpLA0KICAgICAgICAgICAgICAgIHN0YXJ0PW1pbihBY3Rpdml0eVN0YXJ0RGF0ZSksDQogICAgICAgICAgICAgICAgZW5kPW1heChBY3Rpdml0eVN0YXJ0RGF0ZSksDQogICAgICAgICAgICAgICAgbWF4ID0gbWF4KFJlc3VsdE1lYXN1cmVWYWx1ZSwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgICAgICBtZWFuID0gbWVhbihSZXN1bHRNZWFzdXJlVmFsdWUsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICAgICAgc3RkZXYgPSBzZChSZXN1bHRNZWFzdXJlVmFsdWUsIG5hLnJtID0gVFJVRSkpDQpgYGANCg0KICAgIA0KRG90IHBsb3QgdG8gdmlldyB0aGUgIyBvZiBkYXRhIHBvaW50cyBhdCBhIHNpdGUgcmVsYXRpdmUgdG8gcEggc3RkZXZpYXRpb24uIERvIHdlIHdhbnQgdG8gZ2V0IHJpZCBvZiBzaXRlcyB3aXRoIGEgc3RkZXYgPiAxPyANCmBgYHtyIHdhcm5pbmc9RkFMU0V9DQpnZ3Bsb3QoQ1JCX3BIX3N1bW0sIGFlcyh4ID0gY291bnQsIHkgPSBzdGRldikpKw0KICBnZW9tX3BvaW50KCkrDQogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsNSkpDQoNCmBgYA0KICAgDQogICANCldvdywgc29tZSBzaXRlcyBoYXZlIGJlZW4gdmlzaXRlZCBtb3JlIHRoYW4gMTAwMCB0aW1lcy4gIFdoYXQgYXJlIHRoZXNlIHNpdGVzPyBTZWVtcyB0byBiZSBhIG1peCBvZiBsb25nLXRlcm0gbW9uaXRvcmluZyBhbmQgY29udGludW91cyBzb25kZXMuIA0KYGBge3J9DQpDUkJfcEhfc3VtbSAlPiUgDQogIGFycmFuZ2UoZGVzYyhjb3VudCkpICU+JSANCiAgaGVhZChuID0gMjApICAjIGxvb2sgYXQgdGhlIHRvcCAyMCBzaXRlcw0KDQpgYGANCiAgDQogICANCkxvb2sgYXQgZGF0YSBzcGF0aWFsbHkgdXNpbmcgbGVhZmxldC4NCkZpcnN0LCBqb2luIHNwYXRpYWwgaW5mb3IgZnJvbSBzaXRlSW5mbw0KYGBge3Igd2FybmluZz1GQUxTRX0NCkNSQl9wSCA9IENSQl9xdyAlPiUgDQogICAgZmlsdGVyKENoYXJhY3RlcmlzdGljTmFtZSA9PSAicEgiKSAlPiUNCiAgICBmaWx0ZXIoIShSZXN1bHRNZWFzdXJlVmFsdWUgPiAxNHxSZXN1bHRNZWFzdXJlVmFsdWUgPDApKSAlPiUgDQogICAgbXV0YXRlKFJlc3VsdE1lYXN1cmUuTWVhc3VyZVVuaXRDb2RlLCBSZXN1bHRNZWFzdXJlLk1lYXN1cmVVbml0Q29kZSA9ICAgDQogICAgICAgICAgICAgaWZlbHNlKFJlc3VsdE1lYXN1cmUuTWVhc3VyZVVuaXRDb2RlICVpbiUNCiAgICAgICAgICAgICAgICAgICAgIGMoImRlZyBDIiwgIk5UVSIsICJudSIsICIlIiwgIm1nL2wiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1vbGUvbCIsICJzdGQgdW5pdHMiLCAidWcvbCIsICJ1bml0cy9jbSIpLCAiTm9uZSIsDQogICAgICAgICAgICAgICAgICAgIFJlc3VsdE1lYXN1cmUuTWVhc3VyZVVuaXRDb2RlKSkgJT4lDQogICAgbXV0YXRlKE1vbnRoID0gbW9udGgoQWN0aXZpdHlTdGFydERhdGUsIGxhYmVsPVRSVUUpKSAlPiUgDQogICAgbXV0YXRlKE1vbnRoUXVhbGl0eSA9IGlmZWxzZShNb250aCAlaW4lIGMoIkp1biIsICJKdWwiLCAiQXVnIiwgIlNlcCIpLCAiSGlnaFF1YWxpdHkiLCAiTG93UXVhbGl0eSIpKSAlPiUgDQogICAgZmlsdGVyKE1vbnRoUXVhbGl0eSA9PSAiSGlnaFF1YWxpdHkiKSAlPiUNCiAgZ3JvdXBfYnkoTW9uaXRvcmluZ0xvY2F0aW9uSWRlbnRpZmllcikgJT4lDQogICAgICBzdW1tYXJpc2UoY291bnQ9bigpLA0KICAgICAgICAgICAgICAgIHN0YXJ0PW1pbihBY3Rpdml0eVN0YXJ0RGF0ZSksDQogICAgICAgICAgICAgICAgZW5kPW1heChBY3Rpdml0eVN0YXJ0RGF0ZSksDQogICAgICAgICAgICAgICAgbWF4ID0gbWF4KFJlc3VsdE1lYXN1cmVWYWx1ZSwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgICAgICBtZWFuID0gbWVhbihSZXN1bHRNZWFzdXJlVmFsdWUsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICAgICAgc3RkZXYgPSBzZChSZXN1bHRNZWFzdXJlVmFsdWUsIG5hLnJtID0gVFJVRSkpICU+JSANCiAgICBsZWZ0X2pvaW4oc2l0ZUluZm8sIGJ5ID0gIk1vbml0b3JpbmdMb2NhdGlvbklkZW50aWZpZXIiKSAjIGpvaW4gbG9jYXRpb24gaW5mb3JtYXRpb24NCg0KYGBgDQoNCg0KTmV4dCBlbnRlciBwYXJhbWV0ZXJzIGZvciBtYXBwaW5nIG9uIGxlYWZsZXQuDQpZb3UgY2FuIGNoYW5nZSBwSCBicmVha3BvaW50cyBpbiB0aGUgcmlzay5iaW5zIGxpbmUuICANCmBgYHtyfQ0KDQpjb2xfdHlwZXMgPC0gYygiZGFya2JsdWUiLCJvcmFuZ2UiLCJyZWQiLCJicm93biIsInBpbmsiLCJkb2RnZXJibHVlIikgIA0Kcmlzay5iaW5zID0gYygwLCA2LjU5LCA2Ljk5LCA3LjUxLCA3Ljk5LCA4LjUxLCAxNCkNCnJpc2sucGFsID0gY29sb3JCaW4oY29sX3R5cGVzLCBiaW5zID0gcmlzay5iaW5zLCBuYS5jb2xvciA9ICIjYWFmZjU2IikNCnJhZCA8LTMqc2VxKDEsNCxsZW5ndGgub3V0ID0gMTYpDQpDUkJfcEgkc2l6ZXMgPC0gcmFkW2FzLm51bWVyaWMoY3V0KENSQl9wSCRjb3VudCwgYnJlYWtzPTE2KSldDQoNCg0KDQpgYGANCg0KRmluYWxseSwgcGxvdCBpdCBzcGF0aWFsbHkgdmlhIGxlYWZsZXQNCmBgYHtyfQ0KbGlicmFyeShtYXB2aWV3KQ0KDQpwaE1hcCA9IGxlYWZsZXQoZGF0YT1DUkJfcEgpICU+JSANCiAgICAgICNhZGRUaWxlcygpICU+JSBmb3Igc2ltcGxlIG9wZW5zdHJlZXQgYmFzZW1hcA0KICAgICAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkRXNyaS5OYXRHZW9Xb3JsZE1hcCkgJT4lICANCiAgICAgIGFkZENpcmNsZU1hcmtlcnMofmRlY19sb25fdmEsIH5kZWNfbGF0X3ZhLA0KICAgICAgICAgICAgICAgICAgICAgICBmaWxsQ29sb3IgPSB+cmlzay5wYWwobWF4KSwNCiAgICAgICAgICAgICAgICAgICAgICAgcmFkaXVzID0gMiwgICMgdG8gbWFrZSBzaXplIGEgZnVuY3Rpb24gb2YgY291bnQgcmVwbGFjZSB3aXRoIH5zaXplcw0KICAgICAgICAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IDAuOCwgb3BhY2l0eSA9IDAuOCxzdHJva2U9RkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgIHBvcHVwPX5zdGF0aW9uX25tKSAlPiUNCiAgICAgIGFkZExlZ2VuZChwb3NpdGlvbiA9ICdib3R0b21sZWZ0JywNCiAgICAgICAgICAgICAgICBwYWw9cmlzay5wYWwsDQogICAgICAgICAgICAgICAgdmFsdWVzPX5tYXgsDQogICAgICAgICAgICAgICAgb3BhY2l0eSA9IDAuOCwNCiAgICAgICAgICAgICAgICBsYWJGb3JtYXQgPSBsYWJlbEZvcm1hdChkaWdpdHMgPSAxKSwgDQogICAgICAgICAgICAgICAgdGl0bGUgPSAnTWF4IHBIIFZhbHVlJykgJT4lIA0KICBhZGRNb3VzZUNvb3JkaW5hdGVzKHN0eWxlID0gImJhc2ljIikgJT4lIA0KICBzZXRWaWV3KGxuZyA9IC0xMTguMywNCiAgICAgICAgICBsYXQgPSA0NS42LA0KICAgICAgICAgIHpvb20gPSA1LjQ1KQ0KDQpwaE1hcA0KDQpgYGANCg0KDQoNCg0KDQogIA0KRXhwbG9yZSB0aGUgY2FsY2l1bSBkYXRhDQpgYGB7cn0NCkNSQl9DYSA9IENSQl9xdyAlPiUgDQogICAgZmlsdGVyKENoYXJhY3RlcmlzdGljTmFtZSA9PSAiQ2FsY2l1bSIpDQoNCmdncGxvdChDUkJfQ2EsIGFlcyh4ID0gUmVzdWx0TWVhc3VyZS5NZWFzdXJlVW5pdENvZGUsIHkgPSBSZXN1bHRNZWFzdXJlVmFsdWUpKSsNCiAgZ2VvbV9ib3hwbG90KCkNCg0KYGBgDQoNCkFwcGVhcnMgdG8gaGF2ZSBhIGxpdHRsZSBvZiBkaWZmZXJlbnQgdW5pdHMsIHNvIGxldCdzIGNsZWFuIGl0IHVwLg0KMS4gTGltaXQgZGF0YSB0byBqdXN0ICdkaXNzb2x2ZWQnIGFuZCAndG90YWwnLg0KMi4gQ292ZXJ0IHVnL2wgdG8gbWcvbA0KYGBge3J9DQpDUkJfQ2EgPSBDUkJfcXcgJT4lIA0KICBmaWx0ZXIoQ2hhcmFjdGVyaXN0aWNOYW1lID09ICJDYWxjaXVtIikgJT4lIA0KICANCiAgI2dldCByaWQgb2Ygd2hpdGUgc3BhY2VzDQogIG11dGF0ZShSZXN1bHRNZWFzdXJlLk1lYXN1cmVVbml0Q29kZSA9IA0KICAgICAgICAgICAgIHN0cl90cmltKFJlc3VsdE1lYXN1cmUuTWVhc3VyZVVuaXRDb2RlLCBzaWRlID0gImJvdGgiKSkgJT4lICAgDQogICNsaW1pdCB0byBqdXN0IGRpc3NvbHZlZCBhbmQgdG90YWwgQ2ENCiAgZmlsdGVyKFJlc3VsdFNhbXBsZUZyYWN0aW9uVGV4dCAlaW4lIGMoIkRpc3NvbHZlZCIsICJUb3RhbCIpKSAlPiUgICANCiAgDQogICMgY29udmVydCB1Zy9sIHRvIG1nL2wNCiAgbXV0YXRlKFJlc3VsdE1lYXN1cmVWYWx1ZSA9IGNhc2Vfd2hlbihSZXN1bHRNZWFzdXJlLk1lYXN1cmVVbml0Q29kZSA9PSAidWcvbCIgfiAoUmVzdWx0TWVhc3VyZVZhbHVlLzEwMDApLFRSVUV+IGFzLm51bWVyaWMoUmVzdWx0TWVhc3VyZVZhbHVlKSkpICU+JSANCiAgICANCiAgIyByZWNvZGUgdWcvbCB0byBtZy9sDQogIG11dGF0ZShSZXN1bHRNZWFzdXJlLk1lYXN1cmVVbml0Q29kZSwgUmVzdWx0TWVhc3VyZS5NZWFzdXJlVW5pdENvZGUgPQ0KICAgICAgICAgICAgIGlmZWxzZShSZXN1bHRNZWFzdXJlLk1lYXN1cmVVbml0Q29kZSA9PSAidWcvbCIsICJtZy9sIiwNCiAgICAgICAgICAgICAgICAgICAgUmVzdWx0TWVhc3VyZS5NZWFzdXJlVW5pdENvZGUpKQ0KICANCg0KZ2dwbG90KENSQl9DYSwgYWVzKHggPSBSZXN1bHRNZWFzdXJlLk1lYXN1cmVVbml0Q29kZSwgeSA9IFJlc3VsdE1lYXN1cmVWYWx1ZSkpKw0KICBnZW9tX2JveHBsb3QoKQ0KYGBgDQoNCkJldHRlciwgYnV0IHN0aWxsIGEgbG90IG9mIHVuaXRzIHRoYXQgYXJlIG5vdCBtZy9sDQoNCmBgYHtyfQ0KdW5pcXVlKENSQl9DYSRSZXN1bHRNZWFzdXJlLk1lYXN1cmVVbml0Q29kZSkNCmBgYA0KDQpMZXQncyBvbmx5IGtlZXAgdmFsdWUgdGhhdCBoYXZlIG1nL2wgdW5pdHMgYW5kIGFkZCBhIG1vbnRoIGNvbHVtbi4gV2UgbWF5IHdhbnQgdG8gY29uc2lkZXIgY2FsY2l1bSBjYXJib25hdGUgKENhQ08zKSBmb3IgZnV0dXJlIGFuYWx5c2VzIHRob3VnaC4gDQoNCmBgYHtyfQ0KQ1JCX0NhID0gQ1JCX3F3ICU+JSANCiAgZmlsdGVyKENoYXJhY3RlcmlzdGljTmFtZSA9PSAiQ2FsY2l1bSIpICU+JSANCiAgDQogICNnZXQgcmlkIG9mIHdoaXRlIHNwYWNlcw0KICBtdXRhdGUoUmVzdWx0TWVhc3VyZS5NZWFzdXJlVW5pdENvZGUgPSANCiAgICAgICAgICAgICBzdHJfdHJpbShSZXN1bHRNZWFzdXJlLk1lYXN1cmVVbml0Q29kZSwgc2lkZSA9ICJib3RoIikpICU+JSAgIA0KICAjbGltaXQgdG8ganVzdCBkaXNzb2x2ZWQgYW5kIHRvdGFsIENhDQogIGZpbHRlcihSZXN1bHRTYW1wbGVGcmFjdGlvblRleHQgJWluJSBjKCJEaXNzb2x2ZWQiLCAiVG90YWwiKSkgJT4lICAgDQogIA0KICAjIGNvbnZlcnQgdWcvbCB0byBtZy9sDQogIG11dGF0ZShSZXN1bHRNZWFzdXJlVmFsdWUgPSBjYXNlX3doZW4oUmVzdWx0TWVhc3VyZS5NZWFzdXJlVW5pdENvZGUgPT0gInVnL2wiIH4gKFJlc3VsdE1lYXN1cmVWYWx1ZS8xMDAwKSxUUlVFfiBhcy5udW1lcmljKFJlc3VsdE1lYXN1cmVWYWx1ZSkpKSAlPiUgDQogICAgDQogICMgcmVjb2RlIHVnL2wgdG8gbWcvbA0KICBtdXRhdGUoUmVzdWx0TWVhc3VyZS5NZWFzdXJlVW5pdENvZGUsIFJlc3VsdE1lYXN1cmUuTWVhc3VyZVVuaXRDb2RlID0NCiAgICAgICAgICAgICBpZmVsc2UoUmVzdWx0TWVhc3VyZS5NZWFzdXJlVW5pdENvZGUgPT0gInVnL2wiLCAibWcvbCIsDQogICAgICAgICAgICAgICAgICAgIFJlc3VsdE1lYXN1cmUuTWVhc3VyZVVuaXRDb2RlKSkgJT4lIA0KICANCiAgI2xpbWl0IHRvIGp1c3QgbWcvbA0KICBmaWx0ZXIoUmVzdWx0TWVhc3VyZS5NZWFzdXJlVW5pdENvZGUgPT0ibWcvbCIpICU+JSANCiAgDQogICNhZGQgYSBNb250aCBjb2x1bW4gaW4gY2FzZSB3ZSB3YW50IHRvIGV2YWx1YXRlIHNhbXBsaW5nIHRpbWluZw0KICAgIG11dGF0ZShNb250aCA9IG1vbnRoKEFjdGl2aXR5U3RhcnREYXRlLCBsYWJlbD1UUlVFKSkNCiAgDQpnZ3Bsb3QoQ1JCX0NhLCBhZXMoeCA9IFJlc3VsdE1lYXN1cmUuTWVhc3VyZVVuaXRDb2RlLCB5ID0gUmVzdWx0TWVhc3VyZVZhbHVlKSkrDQogIGdlb21fYm94cGxvdCgpDQpgYGANCg0KDQpTaG91bGQgd2UgZ2V0IHJpZGUgb2Ygb3V0bGllcnM/IEhlcmUsIEkgcmVtb3ZlZCBhbnkgdmFsdWUgPiA5Ny41dGggcGVyY2VudGlsZQ0KYGBge3J9DQpDUkJfQ2Ffb3V0ID0gQ1JCX0NhICU+JQ0KICBmaWx0ZXIoIVJlc3VsdE1lYXN1cmVWYWx1ZSA+IHF1YW50aWxlKFJlc3VsdE1lYXN1cmVWYWx1ZSwgMC45NzUpKSANCg0KZ2dwbG90KENSQl9DYV9vdXQsIGFlcyh4ID0gUmVzdWx0TWVhc3VyZS5NZWFzdXJlVW5pdENvZGUsIHkgPSBSZXN1bHRNZWFzdXJlVmFsdWUpKSsNCiAgZ2VvbV9ib3hwbG90KCkNCmBgYA0KDQogIA0KV2hlbiBhcmUgQ2Egc2FtcGxlcyBjb2xsZWN0ZWQ/IE1vc3QgYXJlIGluIHN1bW1lci4uLiBkbyB3ZSB3YW50IHRvIHJlc3RyaWN0IENhIHRvIG5vbi13aW50ZXIgKGkuZS4sIG11c3NlbCBncm93aW5nIHNlYXNvbik/DQpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHBhZ2VkLnByaW50PUZBTFNFfQ0KZ2dwbG90KENSQl9DYV9vdXQsIGFlcyh4ID0gTW9udGgpKSsNCiAgZ2VvbV9oaXN0b2dyYW0oc3RhdD0iY291bnQiKQ0KYGBgDQoNCiAgIA0KSG93IG1hbnkgc2FtcGxlcyBoYXZlIGJlZW4gY29sbGVjdGVkIGF0IGVhY2ggc2l0ZT8gTGV0J3MgZ3JvdXAgdGhlIGRhdGEgYnkgc2l0ZSBhbmQgY2FsY3VsYXRlIHNvbWUgc3VtbWFyaWVzIA0KYGBge3IsIGV2YWw9RkFMU0V9DQpDUkJfQ2Ffc3VtbSA9IENSQl9DYV9vdXQlPiUgDQpncm91cF9ieShNb25pdG9yaW5nTG9jYXRpb25JZGVudGlmaWVyKSAlPiUNCiAgICAgIHN1bW1hcmlzZShjb3VudD1uKCksDQogICAgICAgICAgICAgICAgc3RhcnQ9bWluKEFjdGl2aXR5U3RhcnREYXRlKSwNCiAgICAgICAgICAgICAgICBlbmQ9bWF4KEFjdGl2aXR5U3RhcnREYXRlKSwNCiAgICAgICAgICAgICAgICBtYXggPSBtYXgoUmVzdWx0TWVhc3VyZVZhbHVlLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgICAgIG1lYW4gPSBtZWFuKFJlc3VsdE1lYXN1cmVWYWx1ZSwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgICAgICBzdGRldiA9IHNkKFJlc3VsdE1lYXN1cmVWYWx1ZSwgbmEucm0gPSBUUlVFKSkNCmBgYA0KIA0KICAgIA0KRG90IHBsb3QgdG8gdmlldyB0aGUgIyBvZiBkYXRhIHBvaW50cyBhdCBhIHNpdGUgcmVsYXRpdmUgdG8gcEggc3RkZXZpYXRpb24uIERvIHdlIHdhbnQgdG8gZ2V0IHJpZCBvZiBzaXRlcyB3aXRoIGEgc3RkZXYgPiAxPyANCmBgYHtyIHdhcm5pbmc9RkFMU0V9DQpnZ3Bsb3QoQ1JCX0NhX3N1bW0sIGFlcyh4ID0gY291bnQsIHkgPSBzdGRldikpKw0KICBnZW9tX3BvaW50KCkrDQogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsNjApKQ0KYGBgICAgDQogICANCg0KTG9vayBhdCBkYXRhIHNwYXRpYWxseSB1c2luZyBsZWFmbGV0Lg0KUHV0IGFsbCB0aGUgY3JpdGljYWwgY29kZSB0b2dldGhlciwgdGhlbiBqb2luIHNpdGUgaW5mby4NCmBgYHtyIHdhcm5pbmc9RkFMU0V9DQpDUkJfQ2EgPSBDUkJfcXcgJT4lIA0KICBmaWx0ZXIoQ2hhcmFjdGVyaXN0aWNOYW1lID09ICJDYWxjaXVtIikgJT4lIA0KICBtdXRhdGUoUmVzdWx0TWVhc3VyZS5NZWFzdXJlVW5pdENvZGUgPSANCiAgICAgICAgICAgICBzdHJfdHJpbShSZXN1bHRNZWFzdXJlLk1lYXN1cmVVbml0Q29kZSwgc2lkZSA9ICJib3RoIikpICU+JSAgIA0KICBmaWx0ZXIoUmVzdWx0U2FtcGxlRnJhY3Rpb25UZXh0ICVpbiUgYygiRGlzc29sdmVkIiwgIlRvdGFsIikpICU+JSAgIA0KICBtdXRhdGUoUmVzdWx0TWVhc3VyZVZhbHVlID0gY2FzZV93aGVuKFJlc3VsdE1lYXN1cmUuTWVhc3VyZVVuaXRDb2RlID09ICJ1Zy9sIiANCiAgICAgICAgIH4gKFJlc3VsdE1lYXN1cmVWYWx1ZS8xMDAwKSxUUlVFfiBhcy5udW1lcmljKFJlc3VsdE1lYXN1cmVWYWx1ZSkpKSAlPiUgDQogIG11dGF0ZShSZXN1bHRNZWFzdXJlLk1lYXN1cmVVbml0Q29kZSwgUmVzdWx0TWVhc3VyZS5NZWFzdXJlVW5pdENvZGUgPQ0KICAgICAgICAgICAgIGlmZWxzZShSZXN1bHRNZWFzdXJlLk1lYXN1cmVVbml0Q29kZSA9PSAidWcvbCIsICJtZy9sIiwNCiAgICAgICAgICAgICAgICAgICAgUmVzdWx0TWVhc3VyZS5NZWFzdXJlVW5pdENvZGUpKSAlPiUgDQogIGZpbHRlcihSZXN1bHRNZWFzdXJlLk1lYXN1cmVVbml0Q29kZSA9PSJtZy9sIikgJT4lDQogIGZpbHRlcighUmVzdWx0TWVhc3VyZVZhbHVlID4gcXVhbnRpbGUoUmVzdWx0TWVhc3VyZVZhbHVlLCAwLjk3NSkpICU+JSANCiAgbXV0YXRlKE1vbnRoID0gbW9udGgoQWN0aXZpdHlTdGFydERhdGUsIGxhYmVsPVRSVUUpKSAlPiUgDQogIG11dGF0ZShNb250aFF1YWxpdHkgPSBpZmVsc2UoTW9udGggJWluJSBjKCJKdW4iLCAiSnVsIiwgIkF1ZyIsICJTZXAiKSwgIkhpZ2hRdWFsaXR5IiwgIkxvd1F1YWxpdHkiKSkgJT4lIA0KICAgIGZpbHRlcihNb250aFF1YWxpdHkgPT0gIkhpZ2hRdWFsaXR5IikgJT4lICAjbGltaXQgZGF0YSB0byBzdW1tZXIgbW9udGhzDQogIGdyb3VwX2J5KE1vbml0b3JpbmdMb2NhdGlvbklkZW50aWZpZXIpICU+JQ0KICAgICAgc3VtbWFyaXNlKGNvdW50PW4oKSwNCiAgICAgICAgICAgICAgICBzdGFydD1taW4oQWN0aXZpdHlTdGFydERhdGUpLA0KICAgICAgICAgICAgICAgIGVuZD1tYXgoQWN0aXZpdHlTdGFydERhdGUpLA0KICAgICAgICAgICAgICAgIG1heCA9IG1heChSZXN1bHRNZWFzdXJlVmFsdWUsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICAgICAgbWVhbiA9IG1lYW4oUmVzdWx0TWVhc3VyZVZhbHVlLCBuYS5ybSA9IFRSVUUpKSAlPiUgDQogICAgbGVmdF9qb2luKHNpdGVJbmZvLCBieSA9ICJNb25pdG9yaW5nTG9jYXRpb25JZGVudGlmaWVyIikgIyBqb2luIGxvY2F0aW9uIGluZm9ybWF0aW9uDQpgYGANCg0KDQpOZXh0IGVudGVyIHBhcmFtZXRlcnMgZm9yIG1hcHBpbmcgb24gbGVhZmxldC4NCllvdSBjYW4gY2hhbmdlIENhIGJyZWFrcG9pbnRzIGluIHRoZSByaXNrLmJpbnMgbGluZS4gIEkgZm9sbG93ZWQgV2hpdHRpZXIgZXQgYWwuIDIwMDgsIHdpdGggd2hvICJkZWZpbmVkIHJpc2sgYmFzZWQgb24gY2FsY2l1bSBjb25jZW50cmF0aW9ucyBhczogdmVyeSBsb3cgKDwgMTIgbWcgTD8/PzEpLCBsb3cgKDEyLTIwIG1nIEw/Pz8xKSwgbW9kZXJhdGUgKDIwLTI4IG1nIEw/Pz8xKSwgYW5kIGhpZ2ggKD4gMjggbWcgTD8/PzEpIi4gDQpgYGB7cn0NCg0KY29sX3R5cGVzIDwtIGMoImRhcmtibHVlIiwicGluayIsICJvcmFuZ2UiLCJyZWQiKSAgDQpyaXNrLmJpbnMgPSBjKDAsIDExLjk5LCAxOS45OSwgMjcuOTksIDIwMDAwKQ0Kcmlzay5wYWwgPSBjb2xvckJpbihjb2xfdHlwZXMsIGJpbnMgPSByaXNrLmJpbnMsIG5hLmNvbG9yID0gIiNhYWZmNTYiKQ0KcmFkIDwtMypzZXEoMSw0LGxlbmd0aC5vdXQgPSAxNikNCkNSQl9DYSRzaXplcyA8LSByYWRbYXMubnVtZXJpYyhjdXQoQ1JCX0NhJGNvdW50LCBicmVha3M9MTYpKV0NCg0KDQpgYGANCg0KRmluYWxseSwgcGxvdCBpdCBzcGF0aWFsbHkgdmlhIGxlYWZsZXQNCmBgYHtyfQ0KQ2FNYXAgPSBsZWFmbGV0KGRhdGE9Q1JCX0NhKSAlPiUgDQogICAgICAjYWRkVGlsZXMoKSAlPiUgZm9yIHNpbXBsZSBvcGVuc3RyZWV0IGJhc2VtYXANCiAgICAgIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJEVzcmkuTmF0R2VvV29ybGRNYXApICU+JSAgI1RoaXMgaXMgY29vbC4uLiBodHRwczovL3JzdHVkaW8uZ2l0aHViLmlvL2xlYWZsZXQvYmFzZW1hcHMuaHRtbA0KICAgICAgYWRkQ2lyY2xlTWFya2Vycyh+ZGVjX2xvbl92YSx+ZGVjX2xhdF92YSwNCiAgICAgICAgICAgICAgICAgICAgICAgZmlsbENvbG9yID0gfnJpc2sucGFsKG1heCksDQogICAgICAgICAgICAgICAgICAgICAgIHJhZGl1cyA9IDIsICAjIHRvIG1ha2Ugc2l6ZSBhIGZ1bmN0aW9uIG9mIGNvdW50IHJlcGxhY2Ugd2l0aCB+c2l6ZXMNCiAgICAgICAgICAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLjgsIG9wYWNpdHkgPSAwLjgsc3Ryb2tlPUZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgICBwb3B1cD1+c3RhdGlvbl9ubSkgJT4lDQogICAgICBhZGRMZWdlbmQocG9zaXRpb24gPSAnYm90dG9tbGVmdCcsDQogICAgICAgICAgICAgICAgcGFsPXJpc2sucGFsLA0KICAgICAgICAgICAgICAgIHZhbHVlcz1+bWF4LA0KICAgICAgICAgICAgICAgIG9wYWNpdHkgPSAwLjgsDQogICAgICAgICAgICAgICAgbGFiRm9ybWF0ID0gbGFiZWxGb3JtYXQoZGlnaXRzID0gMSksIA0KICAgICAgICAgICAgICAgIHRpdGxlID0gJ01heCBDYSBWYWx1ZScpICU+JSANCiAgbWFwdmlldzo6YWRkTW91c2VDb29yZGluYXRlcyhzdHlsZSA9ICJiYXNpYyIpICU+JSANCiAgc2V0VmlldyhsbmcgPSAtMTE4LjMsDQogICAgICAgICAgbGF0ID0gNDUuNiwNCiAgICAgICAgICB6b29tID0gNS40NSkNCg0KQ2FNYXANCmBgYA0KDQoNCk15IGF0dGVtcHQgdG8gbG9vayBhdCBib3RoIHRoZSBwSCBhbmQgQ2EgbWFwcw0KYGBge3J9DQpsYXR0aWNldmlldyhDYU1hcCwgcGhNYXApDQpgYGANCg0KDQoNCg0KQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkN0cmwrQWx0K0kqLg0KDQpXaGVuIHlvdSBzYXZlIHRoZSBub3RlYm9vaywgYW4gSFRNTCBmaWxlIGNvbnRhaW5pbmcgdGhlIGNvZGUgYW5kIG91dHB1dCB3aWxsIGJlIHNhdmVkIGFsb25nc2lkZSBpdCAoY2xpY2sgdGhlICpQcmV2aWV3KiBidXR0b24gb3IgcHJlc3MgKkN0cmwrU2hpZnQrSyogdG8gcHJldmlldyB0aGUgSFRNTCBmaWxlKS4NCg0KVGhlIHByZXZpZXcgc2hvd3MgeW91IGEgcmVuZGVyZWQgSFRNTCBjb3B5IG9mIHRoZSBjb250ZW50cyBvZiB0aGUgZWRpdG9yLiBDb25zZXF1ZW50bHksIHVubGlrZSAqS25pdCosICpQcmV2aWV3KiBkb2VzIG5vdCBydW4gYW55IFIgY29kZSBjaHVua3MuIEluc3RlYWQsIHRoZSBvdXRwdXQgb2YgdGhlIGNodW5rIHdoZW4gaXQgd2FzIGxhc3QgcnVuIGluIHRoZSBlZGl0b3IgaXMgZGlzcGxheWVkLg0KDQo=